#include "SysCommons.h"
#include "Element.h"
#include "Table.h"
#include "Caption.h"
#include "Tr.h"
#include "Th.h"
#include "Td.h"
#include "Sorter.h"

#include "Utils.h"

Tr *makeTr(Table *table, Tr::ChildType tr_type) {
	Tr *tr = new Tr(tr_type);
	table->attachChild(tr);
	return tr;
}

Table::Table(std::istream &fin)
:csum(false), rsum(false), border(false) {
	
	char c;
	std::string str;
	bool success = false;
	
	while(!Utils::read(fin, c)) {
		str += c;

		if(str == "]") {
			str.clear();
			success = true;
			break;
		} else if(str == "title:") {
			str.clear();
			attachChild(new Caption(fin));
		} else if(str == "heading:") {
			str.clear();
			readHeadings(fin);
		} else if(str == "row:") {
			str.clear();
			readRows(fin);
		} else if(str == "border:") {
			str.clear();
			border = readBool(fin);
			std::cout << "border: " << border << '\n';
		} else if(str == "csum:") {
			str.clear();
			csum = readBool(fin);
			std::cout << "csum: " << csum << '\n';
		} else if(str == "rsum:") {
			str.clear();
			rsum = readBool(fin);
			std::cout << "rsum: " << rsum << '\n';
		}
	}
	if(!success) {
		throw parsing_error;
	}
	Sorter::table_children_sort(children);

	if(csum) makeCSum();
	if(rsum) makeRSum();
}

void Table::serialize(std::ostream &fout) {
	fout << "<table border='" << (border ? 1 : 0) << "'> \n";
	Element::serialize(fout);
	fout << "</table> \n";
}

void Table::readHeadings(std::istream &fin) {
	char c;
	Tr *tr = makeTr(this, Tr::CT_TH);
	while(!Utils::read(fin, c)) {
		if(c == ';') {
			break;
		} else {
			fin.unget();
			tr->attachChild(new Th(fin));
		}
	}

	if(fin.eof()) {
		throw parsing_error;
	}
}

void Table::readRows(std::istream &fin) {
	char c;
	Tr *tr = makeTr(this, Tr::CT_TD);
	while(fin >> c) {
		if(c == ';') {
			break;
		} else {
			fin.unget();
			tr->attachChild(new Td(fin));
		}
	}

	if(fin.eof()) {
		throw parsing_error;
	}
}

bool Table::readBool(std::istream &fin) {
	bool ret;
	char c;
	std::string val = "";
	while(fin >> c) {
		if(c == ';') 
			break;
		val += c;
	}

	if(val == "yes")
		ret = true;
	else if(val == "no")
		ret = false;
	else
		throw parsing_error;

	if(fin.eof()) {
		throw parsing_error;
	}

	return ret;
}

void Table::makeRSum() {
	bool th = true;

	for(Element::child_it it = child_begin(); it != child_end(); ++it) {
		if(typeid(**it) == typeid(Tr)) {
			Tr *tr = static_cast<Tr *>(*it);
			if(!th) {
				int sum = 0;
				for(Element::child_it it = tr->child_begin(); it != tr->child_end(); ++it) {
					if(typeid(**it) == typeid(Td)) {
						Td *td = static_cast<Td *>(*it);
						if(!td->hasTable()) {
							int val = Utils::stringToInt(td->getValue());
							if(val != std::numeric_limits<int>::signaling_NaN()) {
								sum += val;
							}
						}
					}
				}
				tr->attachChild(new Td(Utils::intToString(sum)));
			} else {
				th = false;
				tr->attachChild(new Th("Sum"));
			}
		}
	}
}

void Table::makeCSum() {
	bool th = true;
	int *csums, cCount = 0;
	for(Element::child_it it = child_begin(); it != child_end(); ++it) {
		if(typeid(**it) == typeid(Tr)) {
			Tr *tr = static_cast<Tr *>(*it);
			if(!th) {
				int i = 0;
				for(Element::child_it it = tr->child_begin(); it != tr->child_end(); ++it, ++i) {
					if(typeid(**it) == typeid(Td)) {
						Td *td = static_cast<Td *>(*it);
						if(!td->hasTable()) {
							int val = Utils::stringToInt(td->getValue());
							if(val != std::numeric_limits<int>::signaling_NaN()) {
								csums[i] += val;
							}
						}
					}
				}
			} else {
				th = false;
				cCount = tr->getChildCount();
				csums = new int[cCount];
				memset(csums, 0, sizeof(int)*cCount);
			}
		}
	}

	Tr *tr = makeTr(this, Tr::CT_TD);
	for(int i = 0 ; i < cCount ; ++i) {
		tr->attachChild(new Td(Utils::intToString(csums[i])));
	}
	delete []csums;
}